home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Entertainment / MacMud / Mud 4.0 / comm1.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-22  |  33.6 KB  |  1,339 lines  |  [TEXT/MPS ]

  1. #include "config.h"
  2.  
  3. #include <types.h>
  4. #include <events.h>
  5. #define    TELOPTS
  6. #include <sys/time.h>
  7. #include <sys/ioctl.h>
  8. #include <sys/types.h>
  9. #include <arpa/telnet.h>
  10. #include <netdb.h>
  11. #include <stdio.h>
  12. #include <errno.h>
  13. #include <string.h>
  14. #include <ctype.h>
  15. #include <signal.h>
  16. #include <memory.h>
  17. #include <fcntl.h>
  18. #include <setjmp.h>
  19. #include "lint.h"
  20. #include "interpret.h"
  21. #include "comm.h"
  22. #include "object.h"
  23. #include "sent.h"
  24. #include "patchlevel.h"
  25. #include "wiz_list.h"
  26. #include "rc.h"
  27.  
  28. void set_prompt(char *);
  29. char *query_ip_number(struct object *);
  30. static void add_ip_entry(long, char *);
  31.  
  32. extern char *xalloc(), *string_copy(), *unshared_str_copy();
  33. extern int d_flag;
  34. extern int current_time;
  35. char * first_cmd_in_buf(struct interactive *);
  36. void next_cmd_in_buf(struct interactive *);
  37. char * skip_eols(struct interactive *, char *);
  38. void remove_flush_entry(struct interactive *ip);
  39.  
  40. void telnet_neg(char *, char *);
  41. void remove_interactive(), add_ref();
  42.  
  43. extern void debug_message(), fatal(), free_sentence();
  44.  
  45. struct interactive **all_players;
  46.  
  47. extern int errno;
  48.  
  49. void new_player();
  50.  
  51. void flush_all_player_mess();
  52.  
  53. extern long DoGetWindow(struct interactive *ip);
  54. extern void DoDisposeWindow(long theWindow);
  55. extern void LPEvents(void);
  56. extern int DoWindowRead(struct interactive *ip);
  57. extern int DoWindowWrite(struct interactive *ip,
  58.                 const char *buf, unsigned long nbyte);
  59. extern Boolean gSocketActive;
  60.  
  61. int num_player;
  62.  
  63. int nfds;
  64. fd_set readfds;
  65.  
  66. #define    StartCmdGiver    (MAX_PLAYERS-1) /* the one after heartbeat */
  67. #define IncCmdGiver    NextCmdGiver = (NextCmdGiver < 0? StartCmdGiver: \
  68.                     NextCmdGiver - 1)
  69.  
  70. int NextCmdGiver;
  71. int CmdsGiven;         /* -1 is used to poll heart beat. */
  72. int twait;             /* wait time for select() */
  73. extern int time_to_call_heart_beat, comm_time_to_call_heart_beat;
  74.  
  75. #ifdef ACCESS_RESTRICTED
  76. extern void *allow_host_access (); 
  77. extern void release_host_access ();
  78. #endif
  79.  
  80. static struct object *first_player_for_flush=(struct object *)NULL;
  81.  
  82. /*
  83.  * Interprocess communication interface to the backend.
  84.  */
  85.  
  86. static int s;
  87. extern int port_number;
  88.  
  89. void PollEvents() {
  90.     extern short __spin_abort;
  91.  
  92.     gSocketActive = true;
  93.     LPEvents();
  94.     gSocketActive = false;
  95. }
  96.  
  97. void prepare_ipc() {
  98.     int tmp;
  99.     char *host_ip;
  100.     struct sockaddr_in sin;
  101.  
  102.     if (MULTI_CONNECTION) {
  103.         s_spinroutine((ProcPtr)PollEvents);
  104.         host_ip = inet_ntoa(xIPAddr());
  105.         memset((char *)&sin, '\0', sizeof sin);
  106.         memcpy((char *)&sin.sin_addr, host_ip, strlen(host_ip));
  107.         sin.sin_port = htons((u_short)port_number);
  108.         sin.sin_family = AF_INET;
  109.         sin.sin_addr.s_addr = INADDR_ANY;
  110.         s = s_socket(AF_INET, SOCK_STREAM, 0);
  111.  
  112.         if (s == -1) {
  113.             perror("socket");
  114.             LPExit(1);
  115.         }
  116.         tmp = 1;
  117.         if (s_bind(s, (struct sockaddr *)&sin, sizeof sin) == -1) {
  118.             if (errno == EADDRINUSE) {
  119.                 fprintf(stderr, "Socket already bound!\n");
  120.                 debug_message("Socket already bound!\n");
  121.                 LPExit(errno);
  122.             } else {
  123.                 perror("bind");
  124.                 LPExit(1);
  125.             }
  126.         }
  127.         if (s_listen(s, 5) == -1) {
  128.             perror("listen");
  129.             LPExit(1);
  130.         }
  131.         tmp = 1;
  132.         if (s_ioctl(s, FIONBIO, &tmp) == -1) {
  133.             perror("ioctl socket FIONBIO");
  134.             LPExit(1);
  135.         }
  136.     }
  137.     start_timer(2);
  138.     twait = 0;
  139.     CmdsGiven = 0;
  140.     time_to_call_heart_beat = 0;
  141.     comm_time_to_call_heart_beat = 0;
  142.     NextCmdGiver = StartCmdGiver;
  143. }
  144.  
  145. /*
  146.  * This one is called when shutting down the MUD.
  147.  */
  148. void ipc_remove() {
  149.     if (MULTI_CONNECTION) {
  150.         s_close(s);
  151.     }
  152. }
  153.  
  154. /*
  155.  * Send a message to a player. If that player is shadowed, special
  156.  * care has to be taken.
  157.  */
  158. /*VARARGS1*/
  159. void add_message(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
  160.     char *fmt;
  161.     int a1, a2, a3, a4, a5, a6, a7, a8, a9;
  162. {
  163.     int from, to;
  164.     int min_length;
  165.     char buff[10000];
  166.     int n, chunk, length;
  167.     int old_message_length;
  168.     struct interactive *ip;
  169.     char buff2[MAX_SOCKET_PACKET_SIZE+1];
  170.  
  171.     if (command_giver == 0 || (command_giver->flags & O_DESTRUCTED) ||
  172.         command_giver->interactive == 0 ||
  173.         command_giver->interactive->do_close)
  174.     {
  175. #if 0
  176.     putchar(']');
  177.     if ( fmt != MESSAGE_FLUSH )
  178.         printf(fmt, a1, a2, a3, a4, a5, a6, a7, a8, a9);
  179.     fflush(stdout);
  180. #endif
  181.     return;
  182.     }
  183.     ip = command_giver->interactive;
  184.     old_message_length = ip->message_length;
  185.     if ( fmt == MESSAGE_FLUSH ) {
  186.         min_length = 1;
  187.         strncpy ( buff, ip->message_buf, length=old_message_length );
  188.         buff[length] = '\0';
  189.     } else {
  190.         min_length = DESIRED_SOCKET_PACKET_SIZE;
  191.         (void)sprintf(buff+old_message_length,fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);
  192.         length = old_message_length + strlen(buff+old_message_length);
  193.         /*
  194.          * Always check that your arrays are big enough ! :-)
  195.          */
  196.         if (length > sizeof buff)
  197.             fatal("To long message!\n");
  198.         if (shadow_catch_message(command_giver, buff+old_message_length))
  199.             return;
  200.         if (ip->snoop_by) {
  201.             struct object *save = command_giver;
  202.             command_giver = ip->snoop_by->ob;
  203.             add_message("%%%s", buff+old_message_length);
  204.             command_giver = save;
  205.         }
  206.         if ( length >= min_length ) {
  207.             strncpy ( buff, ip->message_buf, old_message_length );
  208.         } else {
  209.             strcpy( ip->message_buf+old_message_length,
  210.             buff+old_message_length );
  211.         }
  212.     }
  213.     if (d_flag > 1)
  214.         debug_message("[%s(%d)]: %s", command_giver->name, length, buff);
  215.     /*
  216.      * Insert CR after all NL.
  217.      */
  218.     to = 0;
  219.     for (from = 0; length-from >= min_length; to = 0 ) {
  220.         for ( ; to < (sizeof buff2)-1 && buff[from] != '\0';) {
  221.             if (buff[from] == '\n')
  222.                 buff2[to++] = '\r';
  223.             buff2[to++] = buff[from++];
  224.         }
  225.         if ( to == sizeof(buff2) ) {
  226.             to -= 2;
  227.             from--;
  228.         }
  229.         chunk = to;
  230.         /*
  231.          * We split up the message into something smaller than the max size.
  232.          */
  233.         if (!command_giver->interactive->window) {
  234.             if ((n = s_write(ip->socket, buff2, chunk)) == -1) {
  235.                 if (errno == EMSGSIZE) {
  236.                     fprintf(stderr, "comm1: write EMSGSIZE.\n");
  237.                     return;
  238.                 }
  239.                 if (errno == EINVAL) {
  240.                     fprintf(stderr, "comm1: write EINVAL.\n");
  241.                     if (old_message_length) remove_flush_entry(ip);
  242.                     ip->do_close = 1;
  243.                     return;
  244.                 }
  245.                 if (errno == ENETUNREACH) {
  246.                     fprintf(stderr, "comm1: write ENETUNREACH.\n");
  247.                     if (old_message_length) remove_flush_entry(ip);
  248.                     ip->do_close = 1;
  249.                     return;
  250.                 }
  251.                 if (errno == EHOSTUNREACH) {
  252.                     fprintf(stderr, "comm1: write EHOSTUNREACH.\n");
  253.                     if (old_message_length) remove_flush_entry(ip);
  254.                     ip->do_close = 1;
  255.                     return;
  256.                 }
  257.                 if (errno == EPIPE) {
  258.                     fprintf(stderr, "comm1: write EPIPE detected\n");
  259.                     if (old_message_length) remove_flush_entry(ip);
  260.                     ip->do_close = 1;
  261.                     return;
  262.                 }
  263.                 if (errno == EWOULDBLOCK) {
  264.                     fprintf(stderr, "comm1: write EWOULDBLOCK. Message discarded.\n");
  265.                     if (old_message_length) remove_flush_entry(ip);
  266.             /*        ip->do_close = 1;  -- LA */
  267.                     return;
  268.                 }
  269.                 fprintf(stderr, "write: unknown errno %d\n", errno);
  270.                 perror("write");
  271.                 if (old_message_length) remove_flush_entry(ip);
  272.                 ip->do_close = 1;
  273.                 return;
  274.             }
  275.             if (n != chunk)
  276.                 fprintf(stderr, "write socket: wrote %d, should be %d.\n", n, chunk);
  277.         } else {
  278.             if ((n = DoWindowWrite(command_giver->interactive, buff2, chunk)) == -1) {
  279.                 fprintf(stderr, "comm1: write failed\n");
  280.                 if (old_message_length) remove_flush_entry(ip);
  281.                 ip->do_close = 1;
  282.                 return;
  283.             }
  284.         }
  285.     }
  286.     length -= from;
  287.     ip->message_length=length;
  288.     if (from) strncpy(ip->message_buf, buff+from, length);
  289.     if (length && !old_message_length ) { /* buffer became 'dirty' */
  290.         if (ip->next_player_for_flush = first_player_for_flush ) {
  291.             first_player_for_flush->interactive->previous_player_for_flush =
  292.             command_giver;
  293.         }
  294.         ip->previous_player_for_flush = 0;
  295.         first_player_for_flush = command_giver;
  296.     }
  297.     if ( !length && old_message_length ) { /* buffer has become empty */
  298.         remove_flush_entry(ip);
  299.     }
  300. }
  301.  
  302. void remove_flush_entry(struct interactive *ip)
  303. {
  304.  
  305.     ip->message_length=0;
  306.     if (ip->previous_player_for_flush) {
  307.         ip->previous_player_for_flush->interactive->next_player_for_flush
  308.                 = ip->next_player_for_flush;
  309.     } else {
  310.         first_player_for_flush = ip->next_player_for_flush;
  311.     }
  312.     if (ip->next_player_for_flush) {
  313.         ip->next_player_for_flush->interactive->previous_player_for_flush
  314.             = ip->previous_player_for_flush;
  315.     }
  316. }
  317.  
  318. void flush_all_player_mess() {
  319.     struct object *p,*np;
  320.     struct object *save = command_giver;
  321.  
  322.     for ( p = first_player_for_flush; p; p=np) {
  323.         np = p->interactive->next_player_for_flush;
  324.     /* beware of side-effects when calling add_message the first time! */
  325.         command_giver = p;
  326.         add_message(MESSAGE_FLUSH);
  327.     }
  328.     command_giver=save;
  329. }
  330.  
  331.  
  332. /*
  333.  * Copy a string, replacing newlines with '\0'. Also add an extra
  334.  * space and back space for every newline. This trick will allow
  335.  * otherwise empty lines, as multiple newlines would be replaced by
  336.  * multiple zeroes only.
  337.  */
  338. static int copy_chars(from, to, n)
  339.     char *from, *to;
  340.     int n;
  341. {
  342.     int i;
  343.     char *start = to;
  344.     for (i=0; i<n; i++) {
  345.     if (from[i] == '\r')
  346.         continue;
  347.     if (from[i] == '\n') {
  348.         *to++ = ' ';
  349.         *to++ = '\b';
  350.         *to++ = '\0';
  351.         continue;
  352.     }
  353.     *to++ = from[i];
  354.     }
  355.     return to - start;
  356. }
  357.  
  358.  
  359. /*
  360.  * Get a message from any player.  For all players without a completed
  361.  * cmd in their input buffer, read their socket.  Then, return the first
  362.  * cmd of the next player in sequence that has a complete cmd in their buffer.
  363.  * CmdsGiven is used to allow people in ED to send more cmds (if they have
  364.  * them queued up) than normal players.  If we get a heartbeat, still read
  365.  * all sockets; if the next cmd giver is -1, we have already cycled and
  366.  * can go back to do the heart beat.
  367.  */
  368.  
  369. int get_message(char *buff, int size)
  370. {
  371.     int c;
  372.     char *p;
  373.     int i, res;
  374.     int length;
  375.     int new_socket;
  376.     struct timeval timeout;
  377.     struct interactive *ip = 0;
  378.     struct sockaddr_in addr;
  379.     extern int game_is_being_shut_down;
  380.  
  381.     /*
  382.      * Stay in this loop until we have a message from a player.
  383.      */
  384.     while(!game_is_being_shut_down) {
  385.         LPEvents();
  386.  
  387.         /* First, try to get a new player... */
  388.         if (MULTI_CONNECTION) {
  389.             length = sizeof addr;
  390.             new_socket = s_accept(s, (struct sockaddr *)&addr, &length);
  391.             if (new_socket != -1) {
  392.                 new_player(new_socket, &addr, length);
  393.             } else if (new_socket == -1 && errno != EWOULDBLOCK && errno != EINTR) {
  394.                 perror("accept");
  395.                 LPExit(1);
  396.             }
  397.         }
  398.  
  399.         nfds = 0;
  400.         FD_ZERO(&readfds);
  401.         for (i = 0; i < MAX_PLAYERS; i++) {
  402.             if (!(ip = all_players[i]))
  403.                 continue;
  404.             if (ip->do_close) {
  405.                 ip->do_close = 0;
  406.                 remove_interactive(ip->ob);
  407.                 continue;
  408.             }
  409.             if (!first_cmd_in_buf(ip)) {
  410.                 FD_SET(ip->socket, &readfds);
  411.                 if (ip->socket >= nfds)
  412.                     nfds = ip->socket+1;
  413.             }
  414.         }
  415.  
  416.         res = 0;
  417.         if (MULTI_CONNECTION) {
  418.             timeout.tv_sec = twait; /* avoid busy waiting when no buffered cmds */
  419.             timeout.tv_usec = 0;
  420.             res = s_select(nfds, &readfds, 0, 0, &timeout);
  421.         }
  422.         if (timer_expired()) {
  423.             twait = 0;
  424.             comm_time_to_call_heart_beat = 1;
  425.             goto return_next_command;
  426.         }
  427.         if (res == -1) {
  428.             twait = 0;
  429.             if (errno == EINTR) /* if we got an alarm, finish the round */
  430.                 goto return_next_command;
  431.             perror("select");
  432.             return 0;
  433.         }
  434.         for (i = 0; i < MAX_PLAYERS; i++) { /* read all pending sockets */
  435.             ip = all_players[i];
  436.             if (ip == 0)
  437.                 continue;
  438.             if (!ip->window) {
  439.                 if (res && FD_ISSET(ip->socket, &readfds)) { /* read this player */
  440.                     int l;
  441.  
  442.                     /*
  443.                      * Don't overfill their buffer.
  444.                      * Use a very conservative estimate on how much we can
  445.                      * read.
  446.                      */
  447.                     l = (MAX_TEXT - ip->text_end - 1)/3;
  448.                     if (l < size) size = l;
  449.  
  450.                     if ((l = s_read(ip->socket, buff, size)) == -1) {
  451.                         if (errno == ENETUNREACH) {
  452.                             debug_message("Net unreachable detected.\n");
  453.                             remove_interactive(ip->ob);
  454.                             continue;
  455.                         }
  456.                         if (errno == EHOSTUNREACH) {
  457.                             debug_message("Host unreachable detected.\n");
  458.                             remove_interactive(ip->ob);
  459.                             continue;
  460.                         }
  461.                         if (errno == ETIMEDOUT) {
  462.                             debug_message("Connection timed out detected.\n");
  463.                             remove_interactive(ip->ob);
  464.                             continue;
  465.                         }
  466.                         if (errno == ECONNRESET) {
  467.                             debug_message("Connection reset by peer detected.\n");
  468.                             remove_interactive(ip->ob);
  469.                             continue;
  470.                         }
  471.                         if (errno == EWOULDBLOCK) {
  472.                             debug_message("read would block socket %d!\n", ip->socket);
  473.                             remove_interactive(ip->ob);
  474.                             continue;
  475.                         }
  476.                         if (errno == EMSGSIZE) {
  477.                             debug_message("read EMSGSIZE !\n");
  478.                             continue;
  479.                         }
  480.                         perror("read");
  481.                         debug_message("Unknown errno %d\n", errno);
  482.                         remove_interactive(ip->ob);
  483.                         continue;
  484.                     }
  485.                     if (l == 0) {
  486.                         if (ip->closing)
  487.                             fatal("Tried to read from closing socket.\n");
  488.                         remove_interactive(ip->ob);
  489.                         return 0;
  490.                     }
  491.                     buff[l] = '\0';
  492.                     /* replace newlines by nulls and catenate to buffer */
  493.                     ip->text_end +=
  494.                     copy_chars(buff, ip->text + ip->text_end, l);
  495.                     /* now, text->end is just after the last char read.  If last */
  496.                     /* char was a nl, char *before* text_end will be null. */
  497.                         ip->text[ip->text_end] = '\0';
  498.                 }
  499.             } else {
  500.                 if ((c = DoWindowRead(ip)) != -1) {
  501.                     if (c == '\n') {
  502.                         ip->text[ip->text_end++] = '\0';
  503.                     } else if (c != '\r') {
  504.                         ip->text[ip->text_end++] = c;
  505.                     }
  506.                     ip->text[ip->text_end] = '\0';
  507.                 }
  508.             }
  509.         }
  510.         /*
  511.          * we have read the sockets; now find and return a command
  512.          */
  513.         return_next_command:
  514.         twait = 0;
  515.         ip = 0;
  516.  
  517.         for (i = 0; i < MAX_PLAYERS + 1; i++) {
  518.             if (NextCmdGiver == -1) { /* we have cycled around all players */
  519.                 CmdsGiven = 0;      /* check heart beat */
  520.                 IncCmdGiver;
  521.                 if (comm_time_to_call_heart_beat) {
  522.                     time_to_call_heart_beat = 1; /* twait stays 0! */
  523.                     return 0;
  524.                 }
  525.             }
  526.             ip = all_players[NextCmdGiver];
  527.             if (ip && (p = first_cmd_in_buf(ip))) /* wont respond to partials */
  528.                 break;
  529.             CmdsGiven = 0; /* new player, no cmds issued */
  530.             IncCmdGiver;
  531.         }
  532.  
  533.         if ((!ip) || !p) { /* no cmds found; loop and select (on timeout) again */
  534.             if (comm_time_to_call_heart_beat) { /* may as well do it now */
  535.                 time_to_call_heart_beat = 1; /* no cmds, do heart beat */
  536.                 NextCmdGiver = StartCmdGiver;/* do a complete poll next time */
  537.                 CmdsGiven = 0;
  538.                 return(0);
  539.             }
  540.             /* no heart beat to do and no cmds pending - avoid busy wait on select */
  541.             twait = 1;
  542.             continue; /* else await another cmd */
  543.         }
  544.  
  545.         /*
  546.          * we have a player cmd - return it.  If he is in ed, count his
  547.          * cmds, else only allow 1 cmd.  If he has only one partially
  548.          * completed cmd left after * this, move it to the start of his
  549.          * buffer; new stuff will be appended.
  550.          */
  551.     
  552.         command_giver = ip->ob;
  553.         telnet_neg(buff, p);
  554.         next_cmd_in_buf(ip); /* move on buffer pointers */
  555.  
  556.         /* if he is not in ed, dont let him issue another till the poll comes again */
  557.     
  558.         if (ip->ed_buffer && CmdsGiven < ALLOWED_ED_CMDS) {
  559.             CmdsGiven++;
  560.         } else {
  561.             IncCmdGiver;
  562.             CmdsGiven = 0;
  563.         }
  564.  
  565.         /*
  566.          * Manage snooping - should the snooper see type ahead?  Well, he doesn't here
  567.          */
  568.         if (ip->snoop_by && !ip->noecho) {
  569.             command_giver = ip->snoop_by->ob;
  570.             add_message("%% %s\n", buff);
  571.         }
  572.         command_giver = ip->ob;
  573.  
  574.         if (ip->noecho) {
  575.             /* Must not enable echo before the user input is received. */
  576.             add_message("%c%c%c", IAC, WONT, TELOPT_ECHO);
  577.         }
  578.         ip->noecho = 0;
  579.         ip->last_time = current_time;
  580.         return 1;
  581.     }
  582. }
  583.  
  584. /*
  585.  * find the first character of the next complete cmd in a buffer, 0 if no
  586.  * completed cmd.  There is a completed cmd if there is a null between
  587.  * text_start and text_end.  Zero length commands are discarded (as occur
  588.  * between <cr> and <lf>).  Update text_start if we have to skip leading
  589.  * nulls.
  590.  */
  591.  
  592. char * first_cmd_in_buf(struct interactive *ip) 
  593. {
  594.     char * p, *q;
  595.  
  596.     p = ip->text_start + ip->text;
  597.     while ((p < (ip->text_end + ip->text)) && !*p) /* skip null input */
  598.         p++;
  599.  
  600.     ip->text_start = p - ip->text;
  601.  
  602.     if (ip->text_start >= ip->text_end) {
  603.         ip->text_start = ip->text_end = 0;
  604.         ip->text[0] = '\0';
  605.         return(0);
  606.     }
  607.  
  608.     while ((p < (ip->text_end + ip->text)) && *p) /* find end of cmd */
  609.         p++;
  610.  
  611.     if (p < ip->text + ip->text_end) /* null terminated, was command */
  612.         return(ip->text + ip->text_start);
  613.  
  614. /* have a partial command at end of buffer; move it to start, return null */
  615. /* if it can't move down, truncate it and return it as cmd. */
  616.     
  617.     p = ip->text + ip->text_start;
  618.     q = ip->text;
  619.     while (p < (ip->text + ip->text_end))
  620.         *(q++) = *(p++);
  621.  
  622.     ip->text_end -= ip->text_start;
  623.     ip->text_start = 0;
  624.     if (ip->text_end > MAX_TEXT - 2) {
  625.         ip->text[ip->text_end-2] = '\0'; /* nulls to truncate */
  626.         ip->text[ip->text_end-1] = '\0'; /* nulls to truncate */
  627.         ip->text_end--;
  628.         return(ip->text);
  629.     }
  630. /* buffer not full and no newline - no cmd. */
  631.     return(0);
  632. }
  633.  
  634. /*
  635.  * move pointers to next cmd, or clear buf.
  636.  */
  637.  
  638. void next_cmd_in_buf(ip)
  639. struct interactive *ip;
  640. {
  641.     char * p = ip->text + ip->text_start;
  642.  
  643.     while (*p && p < ip->text + ip->text_end)
  644.         p++;
  645.     /* skip past any nulls at the end */
  646.     while (!*p && p < ip->text + ip->text_end)
  647.         p++;
  648.     if (p < ip->text + ip->text_end) {
  649.         ip->text_start = p - ip->text;
  650.     } else {
  651.         ip->text_start = ip->text_end = 0;
  652.         ip->text[0] = '\0';
  653.     }
  654. }
  655.     
  656. /*
  657.  * Remove an interactive player immediately.
  658.  */
  659. void remove_interactive(ob)
  660.     struct object *ob;
  661. {
  662.     int i;
  663.     extern void save_ed_buffer();
  664.     struct object *save = command_giver;
  665.  
  666.     for (i=0; i<MAX_PLAYERS; i++) {
  667.         if (all_players[i] != ob->interactive)
  668.             continue;
  669.         if (ob->interactive->closing)
  670.             fatal("Double call to remove_interactive()\n");
  671.         ob->interactive->closing = 1;
  672.         if (ob->interactive->snoop_by) {
  673.             ob->interactive->snoop_by->snoop_on = 0;
  674.             ob->interactive->snoop_by = 0;
  675.         }
  676.         if (ob->interactive->snoop_on) {
  677.             ob->interactive->snoop_on->snoop_by = 0;
  678.             ob->interactive->snoop_on = 0;
  679.         }
  680.         command_giver = ob;
  681.         add_message("Closing down.\n");
  682.         if (ob->interactive->ed_buffer) {
  683.             save_ed_buffer();
  684.         }
  685.         add_message(MESSAGE_FLUSH);
  686.         num_player--;
  687.         if (ob->interactive->input_to) {
  688.             free_object(ob->interactive->input_to->ob, "remove_interactive");
  689.             free_sentence(ob->interactive->input_to);
  690.             ob->interactive->input_to = 0;
  691.         }
  692.         if (!ob->interactive->window) {
  693.             s_shutdown(ob->interactive->socket, 2);
  694.             s_close(ob->interactive->socket);
  695. #ifdef ACCESS_RESTRICTED
  696.             release_host_access(ob->interactive->access_class);
  697. #endif
  698.         } else {
  699.             DoDisposeWindow(ob->interactive->window);
  700.         }
  701.         xfree((char *)ob->interactive);
  702.         ob->interactive = 0;
  703.         all_players[i] = 0;
  704.         free_object(ob, "remove_interactive");
  705.         command_giver = save;
  706.         return;
  707.     }
  708.     (void)fprintf(stderr, "Could not find and remove player %s\n", ob->name);
  709.     abort();
  710. }
  711.  
  712. /*
  713.  * get the I'th player object from the interactive list, i starts at 0
  714.  * and can go to num_player - 1.  For users(), etc.
  715.  */
  716. struct object *get_interactive_object(int i)
  717. {
  718.     int n;
  719.  
  720.     if (i >= num_player) /* love them ASSERTS() :-) */
  721.         fatal("Get interactive (%d) with only %d players!", i, num_player);
  722.  
  723.     for (n = 0; n < MAX_PLAYERS; n++)
  724.         if (all_players[n])
  725.             if (!(i--))
  726.                 return(all_players[n]->ob);
  727.  
  728.     fatal("Get interactive: player %d not found! (num_players = %d)",
  729.         i, num_player);
  730.     return 0;    /* Just to satisfy some compiler warnings */
  731. }
  732.  
  733. void new_player(int new_socket, struct sockaddr_in *addr, int len)
  734. {
  735.     int i;
  736.     char *p;
  737.     void *class;
  738.  
  739.     if (new_socket) {
  740. #ifdef ACCESS_RESTRICTED
  741.         if (!(class = allow_host_access(new_socket, new_socket)))
  742.             return;
  743. #endif
  744.         if (d_flag > 1) debug_message("New player at socket %d.\n", new_socket);
  745.     } else {
  746.         if (d_flag > 1) debug_message("New player from console.\n");
  747.     }
  748.     for (i=0; i<MAX_PLAYERS; i++) {
  749.         struct object *ob;
  750.         struct svalue *ret;
  751.         extern struct object *master_ob;
  752.  
  753.         if (all_players[i] != 0)
  754.             continue;
  755.         assert_master_ob_loaded();
  756.         command_giver = master_ob;
  757.         master_ob->interactive =
  758.             (struct interactive *)xalloc(sizeof (struct interactive));
  759.         master_ob->interactive->default_err_message = 0;
  760.         master_ob->flags |= O_ONCE_INTERACTIVE;
  761.         /* This initialization is not pretty. */
  762.         master_ob->interactive->ob = master_ob;
  763.         master_ob->interactive->text[0] = '\0';
  764.         master_ob->interactive->input_to = 0;
  765.         master_ob->interactive->closing = 0;
  766.         master_ob->interactive->snoop_on = 0;
  767.         master_ob->interactive->snoop_by = 0;
  768.         master_ob->interactive->text_end = 0;
  769.         master_ob->interactive->text_start = 0;
  770.         master_ob->interactive->do_close = 0;
  771.         master_ob->interactive->noecho = 0;
  772.         master_ob->interactive->trace_level = 0;
  773.         master_ob->interactive->trace_prefix = 0;
  774.         master_ob->interactive->last_time = current_time;
  775.         master_ob->interactive->ed_buffer = 0;
  776.         master_ob->interactive->message_length=0;
  777.         master_ob->interactive->window = 0;
  778.         master_ob->interactive->socket = 0;
  779.         all_players[i] = master_ob->interactive;
  780.         set_prompt("> ");
  781.  
  782.         if (new_socket) {
  783.             all_players[i]->socket = new_socket;
  784.             memcpy((char *)&all_players[i]->addr, (char *)addr, len);
  785. #ifdef ACCESS_RESTRICTED
  786.             all_players[i]->access_class = class;
  787. #endif
  788.         } else {
  789.             master_ob->interactive->window = DoGetWindow(master_ob->interactive);
  790.             if (!master_ob->interactive->window) {
  791.                 remove_interactive(master_ob);
  792.                 return;
  793.             }
  794.         }
  795.         num_player++;
  796.         /*
  797.          * The player object has one extra reference.
  798.          * It is asserted that the master_ob is loaded.
  799.          */
  800.         add_ref(master_ob, "new_player");
  801.         ret = apply_master_ob("connect", 0);
  802.         if (ret == 0 || ret->type != T_OBJECT) {
  803.             remove_interactive(master_ob);
  804.             return;
  805.         }
  806.         /*
  807.          * There was an object returned from connect(). Use this as the
  808.          * player object.
  809.          */
  810.         ob = ret->u.ob;
  811.         ob->interactive = master_ob->interactive;
  812.         ob->interactive->ob = ob;
  813.         ob->flags |= O_ONCE_INTERACTIVE;
  814.         master_ob->flags &= ~O_ONCE_INTERACTIVE;
  815.         add_message(MESSAGE_FLUSH);
  816.         master_ob->interactive = 0;
  817.         free_object(master_ob, "reconnect");
  818.         add_ref(ob, "new_player");
  819.         command_giver = ob;
  820.         logon(ob);
  821.         flush_all_player_mess();
  822.         return;
  823.     }
  824.     if (new_socket) {
  825.         p = "Lpmud is full. Come back later.\r\n";
  826.         s_write(new_socket, p, strlen(p));
  827.         s_close(new_socket);
  828.     } else {
  829.     }
  830. }
  831.  
  832. int call_function_interactive(struct interactive *i, char *str)
  833. {
  834.     char *function;
  835.     struct object *ob;
  836.  
  837.     if (!i->input_to)
  838.         return 0;
  839.     /*
  840.      * Special feature: input_to() has been called to setup
  841.      * a call to a function.
  842.      */
  843.     if (i->input_to->ob->flags & O_DESTRUCTED) {
  844.         /* Sorry, the object has selfdestructed ! */
  845.         free_object(i->input_to->ob, "call_function_interactive");
  846.         free_sentence(i->input_to);
  847.         i->input_to = 0;
  848.         return 0;
  849.     }
  850.     free_object(i->input_to->ob, "call_function_interactive");
  851.     function = string_copy(command_giver->interactive->input_to->function);
  852.     ob = i->input_to->ob;
  853.     free_sentence(i->input_to);
  854.     /*
  855.      * We must clear this reference before the call to apply(), because
  856.      * someone might want to set up a new input_to().
  857.      */
  858.     i->input_to = 0;
  859.     /*
  860.      * Now we set current_object to this object, so that input_to will
  861.      * work for static functions.
  862.      */
  863.     push_constant_string(str);
  864.     current_object = ob;
  865.     (void)apply(function, ob, 1);
  866.     xfree(function);
  867.     flush_all_player_mess();
  868.     return 1;
  869. }
  870.  
  871. int set_call(struct object *ob, struct sentence *sent, int noecho)
  872. {
  873.     struct object *save = command_giver;
  874.     if (ob == 0 || sent == 0)
  875.         return 0;
  876.     if (ob->interactive == 0 || ob->interactive->input_to)
  877.         return 0;
  878.     ob->interactive->input_to = sent;
  879.     ob->interactive->noecho = noecho;
  880.     command_giver = ob;
  881.     if (noecho)
  882.     add_message("%c%c%c", IAC, WILL, TELOPT_ECHO);
  883.     command_giver = save;
  884.     return 1;
  885. }
  886.  
  887. void show_info_about(char *str, char *room, struct interactive *i)
  888. {
  889.     add_message("%-15s %s\n", str, room);
  890. }
  891.  
  892. void remove_all_players()
  893. {
  894.     int i;
  895.     extern jmp_buf error_recovery_context;
  896.     extern int error_recovery_context_exists;
  897.  
  898.     for (i=0; i<MAX_PLAYERS; i++) {
  899.         if (all_players[i] == 0)
  900.             continue;
  901.         command_giver = all_players[i]->ob;
  902.         error_recovery_context_exists = 2;
  903.         push_pop_error_context(1);
  904.         if (!setjmp(error_recovery_context)) {
  905.             (void)apply("quit", all_players[i]->ob, 0);
  906.         } else {
  907.             push_pop_error_context(-1);
  908.         }
  909.     }
  910. }
  911.  
  912. void set_prompt(char *str)
  913. {
  914.     command_giver->interactive->prompt = str;
  915. }
  916.  
  917. /*
  918.  * Print the prompt, but only if input_to not is disabled.
  919.  */
  920. void print_prompt()
  921. {
  922.     if (command_giver == 0)
  923.         fatal("command_giver == 0.\n");
  924.     if (command_giver->interactive->input_to == 0) {
  925.         add_message(command_giver->interactive->prompt);
  926.         if (1) { /* add test for heart_beat later */
  927.             flush_all_player_mess();
  928.         }
  929.     }
  930. }
  931.  
  932. /*
  933.  * Let object 'me' snoop object 'you'. If 'you' is 0, then turn off
  934.  * snooping.
  935.  */
  936. void set_snoop(struct object *me, struct object *you)
  937. {
  938.     int i;
  939.     struct interactive *on = 0, *by = 0, *tmp;
  940.  
  941.     if (me->flags & O_DESTRUCTED)
  942.         return;
  943.     if (you && (you->flags & O_DESTRUCTED))
  944.         return;
  945.     for(i=0; i<MAX_PLAYERS && (on == 0 || by == 0); i++) {
  946.         if (all_players[i] == 0)
  947.             continue;
  948.         if (all_players[i]->ob == me)
  949.             by = all_players[i];
  950.         else if (all_players[i]->ob == you)
  951.             on = all_players[i];
  952.     }
  953.     if (you == 0) {
  954.         if (by == 0)
  955.             error("Could not find myself to stop snoop.\n");
  956.         add_message("Ok.\n");
  957.         if (by->snoop_on == 0)
  958.             return;
  959.         by->snoop_on->snoop_by = 0;
  960.         by->snoop_on = 0;
  961.         return;
  962.     }
  963.     if (on == 0 || by == 0) {
  964.         add_message("Failed.\n");
  965.         return;
  966.     }
  967.     if (by->snoop_on) {
  968.         by->snoop_on->snoop_by = 0;
  969.         by->snoop_on = 0;
  970.     }
  971.     if (on->snoop_by) {
  972.         add_message("Busy.\n");
  973.         return;
  974.     }
  975.     /*
  976.      * Protect against snooping loops.
  977.      */
  978.     for (tmp = on; tmp; tmp = tmp->snoop_on) {
  979.         if (tmp == by) {
  980.             add_message("Busy.\n");
  981.             return;
  982.         }
  983.     }
  984.     on->snoop_by = by;
  985.     by->snoop_on = on;
  986.     add_message("Ok.\n");
  987.     return;
  988. }
  989.  
  990. /*
  991.  * Let object 'me' snoop object 'you'. If 'you' is 0, then turn off
  992.  * snooping.
  993.  *
  994.  * This routine is almost identical to the old set_snoop. The main
  995.  * difference is that the routine writes nothing to player directly,
  996.  * all such communication is taken care of by the mudlib. It communicates
  997.  * with master.c in order to find out if the operation is permissble or
  998.  * not. The old routine let everyone snoop anyone. This routine also returns
  999.  * 0 or 1 depending on success.
  1000.  */
  1001. int new_set_snoop(struct object *me, struct object *you)
  1002. {
  1003.     int i;
  1004.     struct svalue *ret;
  1005.     struct interactive *on = 0, *by = 0, *tmp;
  1006.  
  1007.     /* Stop if people managed to quit before we got this far */
  1008.     if (me->flags & O_DESTRUCTED)
  1009.         return 0;
  1010.     if (you && (you->flags & O_DESTRUCTED))
  1011.         return 0;
  1012.  
  1013.     /* Find the snooper & snopee */
  1014.     for(i = 0 ; i < MAX_PLAYERS && (on == 0 || by == 0); i++) {
  1015.         if (all_players[i] == 0)
  1016.             continue;
  1017.         if (all_players[i]->ob == me)
  1018.             by = all_players[i];
  1019.         else if (all_players[i]->ob == you)
  1020.             on = all_players[i];
  1021.     }
  1022.  
  1023.     /* Illegal snoop attempt by null object */
  1024.     if (!current_object->eff_user)
  1025.         return 0;
  1026.  
  1027.     /* Check for permissions with valid_snoop in master */
  1028.     push_object(me);
  1029.     if (you == 0) {
  1030.         push_number(0);
  1031.     } else {
  1032.         push_object(you);
  1033.     }
  1034.     ret = apply_master_ob("valid_snoop", 2);
  1035.  
  1036.     if (!ret || ret->type != T_NUMBER || ret->u.number == 0)
  1037.         return 0;
  1038.  
  1039.     /* Stop snoop */
  1040.     if (you == 0) {
  1041.         if (by == 0)
  1042.             error("Could not find snooper to stop snoop on.\n");
  1043.         if (by->snoop_on == 0)
  1044.             return 1;
  1045.         by->snoop_on->snoop_by = 0;
  1046.         by->snoop_on = 0;
  1047.         return 1;
  1048.     }
  1049.  
  1050.     /* Strange event, but possible, so test for it */
  1051.     if (on == 0 || by == 0)
  1052.         return 0;
  1053.  
  1054.     /* Protect against snooping loops */
  1055.     for (tmp = on; tmp; tmp = tmp->snoop_on) {
  1056.         if (tmp == by) 
  1057.             return 0;
  1058.     }
  1059.  
  1060.     /* Terminate previous snoop, if any */
  1061.     if (by->snoop_on) {
  1062.         by->snoop_on->snoop_by = 0;
  1063.         by->snoop_on = 0;
  1064.     }
  1065.     if (on->snoop_by) {
  1066.         on->snoop_by->snoop_on = 0;
  1067.         on->snoop_by = 0;
  1068.     }
  1069.     on->snoop_by = by;
  1070.     by->snoop_on = on;
  1071.     return 1;
  1072.     
  1073. }
  1074.  
  1075. #define    TS_DATA        0
  1076. #define    TS_IAC        1
  1077. #define    TS_WILL        2
  1078. #define    TS_WONT        3
  1079. #define    TS_DO        4
  1080. #define    TS_DONT        5
  1081.  
  1082. void telnet_neg(char *to, char *from)
  1083. {
  1084.     int ch;
  1085.     int state = TS_DATA;
  1086.     char *first = to;
  1087.  
  1088.     while(1) {
  1089.         ch = (*from++ & 0xff);
  1090.         switch(state) {
  1091.         case TS_DATA:
  1092.             switch(ch) {
  1093.                 case IAC:
  1094.                     state = TS_IAC;
  1095.                     continue;
  1096.                 case '\b':    /* Backspace */
  1097.                 case 0x7f:    /* Delete */
  1098.                     if (to <= first)
  1099.                         continue;
  1100.                     to -= 1;
  1101.                     continue;
  1102.                 default:
  1103.                     if (ch & 0x80) {
  1104.                         if (d_flag) debug_message("Tel_neg: 0x%x\n", ch);
  1105.                         continue;
  1106.                     }
  1107.                     *to++ = ch;
  1108.                     if (ch == 0)
  1109.                         return;
  1110.                     continue;
  1111.             }
  1112.         case TS_IAC:
  1113.             switch(ch) {
  1114.                 case WILL:
  1115.                     state = TS_WILL;
  1116.                     continue;
  1117.                 case WONT:
  1118.                     state = TS_WONT;
  1119.                     continue;
  1120.                 case DO:
  1121.                     state = TS_DO;
  1122.                     continue;
  1123.                 case DONT:
  1124.                     state = TS_DONT;
  1125.                     continue;
  1126.                 case DM:
  1127.                     break;
  1128.                 case NOP:
  1129.                 case GA:
  1130.                 default:
  1131.                     break;
  1132.             }
  1133.             state = TS_DATA;
  1134.             continue;
  1135.         case TS_WILL:
  1136.             if (d_flag) debug_message("Will %s\n", telopts[ch]);
  1137.             state = TS_DATA;
  1138.             continue;
  1139.         case TS_WONT:
  1140.             if (d_flag) debug_message("Wont %s\n", telopts[ch]);
  1141.             state = TS_DATA;
  1142.             continue;
  1143.         case TS_DO:
  1144.             if (d_flag) debug_message("Do %s\n", telopts[ch]);
  1145.             state = TS_DATA;
  1146.             continue;
  1147.         case TS_DONT:
  1148.             if (d_flag) debug_message("Dont %s\n", telopts[ch]);
  1149.             state = TS_DATA;
  1150.             continue;
  1151.         default:
  1152.             if (d_flag) debug_message("Bad state: 0x%x\n", state);
  1153.             state = TS_DATA;
  1154.             continue;
  1155.         }
  1156.     }
  1157. }
  1158.  
  1159. #define IPSIZE 200
  1160. static struct ipentry {
  1161.     long addr;
  1162.     char *name;
  1163. } iptable[IPSIZE];
  1164. static int ipcur;
  1165.  
  1166. char *query_ip_name(struct object *ob)
  1167. {
  1168.     int i;
  1169.  
  1170.     if (ob == 0)
  1171.         ob = command_giver;
  1172.     if (!ob || ob->interactive == 0)
  1173.         return 0;
  1174.     if (!ob->interactive->window) {
  1175.         for(i = 0; i < IPSIZE; i++) {
  1176.             if (iptable[i].addr == ob->interactive->addr.sin_addr.s_addr && iptable[i].name)
  1177.                 return iptable[i].name;
  1178.         }
  1179.         return inet_ntoa(ob->interactive->addr.sin_addr);
  1180.     } else {
  1181.         return MUDHOSTNAME;
  1182.     }
  1183. }
  1184.  
  1185. static void add_ip_entry(long addr, char *name)
  1186. {
  1187.     int i;
  1188.  
  1189.     for(i = 0; i < IPSIZE; i++) {
  1190.     if (iptable[i].addr == addr)
  1191.         return;
  1192.     }
  1193.     iptable[ipcur].addr = addr;
  1194.     if (iptable[ipcur].name)
  1195.     free_string(iptable[ipcur].name);
  1196.     iptable[ipcur].name = make_shared_string(name);
  1197.     ipcur = (ipcur+1) % IPSIZE;
  1198. }
  1199.  
  1200. static char ipnum[] = "1.1.1.1";
  1201.  
  1202. char *query_ip_number(struct object *ob)
  1203. {
  1204.     if (ob == 0)
  1205.         ob = command_giver;
  1206.     if (!ob || ob->interactive == 0)
  1207.         return 0;
  1208.     if (!ob->interactive->window) {
  1209.         return inet_ntoa(ob->interactive->addr.sin_addr);
  1210.     } else {
  1211.         return ipnum;
  1212.     }
  1213. }
  1214.  
  1215. char *query_host_name() {
  1216.     static char name[100];
  1217.  
  1218.     if (MULTI_CONNECTION) {
  1219.         gethostname(name, sizeof name);
  1220.         name[sizeof name - 1] = '\0';    /* Just to make sure */
  1221.         return name;
  1222.     }
  1223.     return MUDHOSTNAME;
  1224. }
  1225.  
  1226. struct object *query_snoop(ob)
  1227.     struct object *ob;
  1228. {
  1229.     if (ob->interactive->snoop_by == 0)
  1230.         return 0;
  1231.     return ob->interactive->snoop_by->ob;
  1232. }
  1233.  
  1234. int query_idle(ob)
  1235.     struct object *ob;
  1236. {
  1237.     if (!ob->interactive)
  1238.         error("query_idle() of non-interactive object.\n");
  1239.     return current_time - ob->interactive->last_time;
  1240. }
  1241.  
  1242. void notify_no_command() {
  1243.     char *p,*m;
  1244.  
  1245.     if (!command_giver->interactive)
  1246.         return;
  1247.     p = command_giver->interactive->default_err_message;
  1248.     if (p) {
  1249.         m = process_string(p); /* We want 'value by function call' /JnA */
  1250.         if (!shadow_catch_message(command_giver, m))
  1251.             add_message(m);
  1252.         if (m != p)
  1253.             xfree(m);
  1254.         free_string(p);
  1255.         command_giver->interactive->default_err_message = 0;
  1256.     } else {
  1257.         add_message(WHAT_MESSAGE);
  1258.     }
  1259. }
  1260.  
  1261. void clear_notify() {
  1262.     if (!command_giver->interactive)
  1263.         return;
  1264.     if (command_giver->interactive->default_err_message) {
  1265.         free_string(command_giver->interactive->default_err_message);
  1266.         command_giver->interactive->default_err_message = 0;
  1267.     }
  1268. }
  1269.  
  1270. void set_notify_fail_message(str)
  1271.     char *str;
  1272. {
  1273.     if (!command_giver || !command_giver->interactive)
  1274.         return;
  1275.     clear_notify();
  1276.     if (command_giver->interactive->default_err_message)
  1277.         free_string(command_giver->interactive->default_err_message);
  1278.     command_giver->interactive->default_err_message = make_shared_string(str);
  1279. }
  1280.  
  1281. int replace_interactive(ob, obfrom, /*IGN*/name)
  1282.     struct object *ob;
  1283.     struct object *obfrom;
  1284.     char *name;
  1285. {
  1286.     /* marion
  1287.      * i see no reason why to restrict this, besides - the length
  1288.      * (was) missing to strncmp()
  1289.      * JnA: There is every reason to restrict this.
  1290.      *      Otherwise I can write my own player object without any security
  1291.      *      at all!
  1292.      */
  1293.     struct svalue *v;
  1294.  
  1295.     push_string(name, STRING_CONSTANT);
  1296.     v = apply_master_ob("valid_exec", 1);
  1297.     if (!v || v->type != T_NUMBER || v->u.number == 0)
  1298.         return 0;
  1299.     /* fprintf(stderr,"DEBUG: %s,%s\n",ob->name,obfrom->name); */
  1300.     if (ob->interactive)
  1301.         error("Bad argument1 to exec()\n");
  1302.     if (!obfrom->interactive)
  1303.         error("Bad argument2 to exec()\n");
  1304.     if (obfrom->interactive->message_length) {
  1305.         struct object *save;
  1306.         save=command_giver;
  1307.         command_giver=obfrom;
  1308.         add_message(MESSAGE_FLUSH);
  1309.         command_giver=save;
  1310.     }
  1311.     ob->interactive = obfrom->interactive;
  1312.     obfrom->interactive = 0;
  1313.     ob->interactive->ob = ob;
  1314.     ob->flags |= O_ONCE_INTERACTIVE;
  1315.     obfrom->flags &= ~O_ONCE_INTERACTIVE;
  1316.     add_ref(ob, "exec");
  1317.     free_object(obfrom, "exec");
  1318.     if (obfrom == command_giver) command_giver = ob;
  1319.         return 1;
  1320. }
  1321.  
  1322. #ifdef DEBUG
  1323. /*
  1324.  * This is used for debugging reference counts.
  1325.  */
  1326.  
  1327. void update_ref_counts_for_players() {
  1328.     int i;
  1329.  
  1330.     for (i=0; i<MAX_PLAYERS; i++) {
  1331.     if (all_players[i] == 0)
  1332.         continue;
  1333.     all_players[i]->ob->extra_ref++;
  1334.     if (all_players[i]->input_to)
  1335.         all_players[i]->input_to->ob->extra_ref++;
  1336.     }
  1337. }
  1338. #endif /* DEBUG */
  1339.